// Socket.h

#pragma once
#ifndef SOCKET_H_INCLUDED
#define SOCKET_H_INCLUDED

class WinSock
{
	static int s_iInitCnt;
public:
	static bool Init();
	static void Shut();

	static int GetLastError();
	static void ErrorCodeToText(int p_iError,char* p_sDest,int p_iDestSize);
};

#pragma pack(push)
#pragma pack(1)

#ifdef _WINSOCK2API_
class IPAddr : public SOCKADDR_IN
{
public:
#else
class IPAddr
{
public:
	int16			sin_family;
	uint16			sin_port;
	struct {
		union {
			struct { uint8 s_b1,s_b2,s_b3,s_b4; } S_un_b;
			struct { uint16 s_w1,s_w2; } S_un_w;
			struct { uint32 s_dw; } S_un_dw;
			dword	S_addr;
		} S_un;
	}				sin_addr;
	char			sin_zero[8];
#endif

	IPAddr();
	IPAddr(const IPAddr& p_xA);
	IPAddr& operator=(const IPAddr& p_xA);

	IPAddr(const char* p_sHost,int p_iPort);
	IPAddr(const char* p_sHostAndPort);
	IPAddr& operator=(const char* p_sHostAndPort);

	bool operator==(const IPAddr& p_xA) const;
	bool operator!=(const IPAddr& p_xA) const;

	bool Set(const char* p_sHost,int p_iPort=-1);

	bool IsValid() const {return sin_addr.S_un.S_addr!=0;};
	int GetHost() const {return sin_addr.S_un.S_addr;};
	void SetHost(int p_iHost) {sin_addr.S_un.S_addr=p_iHost;};

	void SetPort(int p_iPort);
	int GetPort() const;

	static IPAddr GetLocalDefault();

	void ToText(char* po_sDest,int p_iSize);
	AStr ToStr() const;
};

class TcpSocket
{
private:
	TcpSocket(const TcpSocket&);
	TcpSocket& operator=(const TcpSocket&);
public:
	int m_iSock;
	void* m_hCloseEvt;
	int m_iLastError;

	TcpSocket();
	~TcpSocket();

	bool Connect(const IPAddr& p_xAddr,int p_iTimeoutMS=-1);
	void Close();

	int Send(const void* p_pData,int p_iSize);
	int Recv(void* p_pData,int p_iSize);

	bool CanSend();
	bool CanRecv();
	int PendingRecv();

	IPAddr GetRemoteAddr() const;
	IPAddr GetLocalAddr() const;

	bool HasHalfClose();

	bool WaitForRecvData(int p_iTimeoutMS);

	bool IsConnected() const;

	void Swap(TcpSocket& po_xOther);
};

class TcpAcceptSocket
{
private:
	TcpAcceptSocket(const TcpAcceptSocket&);
	TcpAcceptSocket& operator=(const TcpAcceptSocket&);
public:
	int m_iSock;
	int m_iLastError;
	void* m_hConReqEvt;

	TcpAcceptSocket();
	~TcpAcceptSocket();

	bool Open(const IPAddr& p_xAddr,int p_iMaxPending=-1);
	void Close();

	bool ConnectionPending(int p_iTimeout);
	bool Accept(TcpSocket& po_xNewCon);
};

class UdpSocket
{
public:
	int m_iSock;
	void* m_hReadData;

	UdpSocket();
	~UdpSocket();

	void Close();
	bool Open();
	bool Open(const IPAddr& p_xAddr);

	int SendTo(const IPAddr& p_xRemoteAddr,void* p_pData,int p_iSize);
	int Recv(void* p_pData,int p_iSize);
	int RecvFrom(IPAddr& po_xRemoteAddr,void* p_pData,int p_iSize);

	int PendingRecv();

	bool IsOpen() const;
};

#pragma pack(pop)

#endif // SOCKET_H_INCLUDED
